#include <stdio.h>
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <assert.h>

#include "hw2_syscalls.h"

int fibonaci(unsigned int n)
{
	if (n < 2) {
		return n;
	}
	return fibonaci(n-2) + fibonaci(n-1);
}

void task(int nr_trials, int n)
{
	struct sched_param lp;
	lp.sched_priority = 0;
	lp.trial_num = nr_trials;
	sched_setscheduler(getpid(), SCHED_BRR, &lp);

	fibonaci(n);
}

const char * reason_to_str(enum sched_reason reason)
{
	switch (reason) {
	case SCHED_REASON_TASK_CREATE:
		return "SCHED_REASON_TASK_CREATE";
	case SCHED_REASON_TASK_END:
		return "SCHED_REASON_TASK_END";
	case SCHED_REASON_TASK_YIELD:
		return "SCHED_REASON_TASK_YIELD";
	case SCHED_REASON_BRR_OVERDUE:
		return "SCHED_REASON_BRR_OVERDUE";
	case SCHED_REASON_TASK_SLEEP:
		return "SCHED_REASON_TASK_SLEEP";
	case SCHED_REASON_PREEMPT:
		return "SCHED_REASON_PREEMPT";
	case SCHED_REASON_TIMESLICE:
		return "SCHED_REASON_TIMESLICE";
	case SCHED_REASON_IDLE:
		return "SCHED_REASON_IDLE";
	default:
		return "SCHED_REASON_INVALID";
	}
}

const char * policy_to_str(int policy)
{
	switch (policy) {
	case SCHED_OTHER:
		return "SCHED_OTHER";
	case SCHED_BRR:
		return "SCHED_BRR";
	case SCHED_RR:
		return "SCHED_RR";
	case SCHED_FIFO:
		return "SCHED_FIFO";
	default:
		return "SCHED_WTF";
	}
}

void print_stats()
{
	struct switch_info info[150];
	int i, ret;

	if (!(ret = get_scheduling_statistic(info))) {
		perror("Can't get stats");
		exit(1);
	}
	for (i = 0; i < ret; ++i) {
		printf("%10lu: [%5d|%11s]->[%5d|%11s] = %s\n",
		       info[i].time,
		       info[i].previous_pid,
		       policy_to_str(info[i].previous_policy),
		       info[i].next_pid,
		       policy_to_str(info[i].next_policy),
		       reason_to_str(info[i].reason));
	}
}

int main(int argc, char ** argv)
{
	int i;
	assert(SCHED_BRR == 4);
	if ((argc & 0x1) == 0) {
		fprintf(stderr, "Usage: %s <nr_trials1> <n1> ...\n", argv[0]);
		exit(1);
	}

	for (i = (argc >> 1); i > 0; --i) {
		pid_t pid;
		int nr_trials, n;
		nr_trials = atoi(argv[(i<<1)-1]);
		n = atoi(argv[(i<<1)]);

		if (!(pid = fork())) {
			task(nr_trials, n);
			exit(0);
		}
		if (pid < 0) {
			perror("Can't fork");
			exit(1);
		}
		if (pid > 0) {
			printf("Creating: PID %d: nr_trials=%d, n=%d\n", (int)pid, nr_trials, n);
		}

		/* waitpid(pid, NULL, 0); */
	}

	fflush(stdout);

	while (wait(NULL) != -1) {}

	print_stats();
	return 0;
}
